package org.example.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.example.component.BlockingQueueConsumer;
import org.example.pojo.Result;
import org.example.pojo.SeckillActivity;
import org.example.pojo.SeckillOrder;
import org.example.service.SeckillActivityService;
import org.example.service.SeckillOrderService;
import org.example.utils.RedisIdWorker;
import org.example.utils.RedisLock;
import org.json.JSONObject;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("redisson")
public class RedissonController
{
	@Autowired
	SeckillOrderService seckillOrderService;
	@Autowired
	SeckillActivityService seckillActivityService;
	
	@Autowired
	RedisIdWorker redisIdWorker;
	
	@Autowired
	private RedissonClient redissonClient;
	
	/**
	 * 集群模式解决方案，使用redis互斥锁
	 */
	@GetMapping("seckillCouponRedis")
	public Result seckillCouponRedis(@RequestParam Long coupon_id, @RequestParam Long uid) throws InterruptedException {
		//查询活动信息
		QueryWrapper qw = new QueryWrapper();
		qw.eq("coupon_id",coupon_id);
		SeckillActivity one = seckillActivityService.getOne(qw);
		if(one == null)
		{
			throw new RuntimeException("活动不存在");
		}

		LocalDateTime now = LocalDateTime.now();//当前时间
		//判断秒杀活动是否开始
		if(now.isBefore(one.getStartTime()))
		{
			throw new RuntimeException("活动未开始");
		}
		//判断秒杀活动是否结束
		if(now.isAfter(one.getEndTime()))
		{
			throw new RuntimeException("活动已结束");
		}

		//判断库存是否充足
		Integer totalStock = one.getTotalStock();
		if(totalStock < 0)
		{
			throw  new RuntimeException("库存不足");
		}
		//一人一单处理，禁止黄牛行为
		//创建锁
		RLock lock = redissonClient.getLock("myLock");
		//上锁，失败则返回错误，参数为：获取锁的等待时间，锁自动释放时间，时间单位
		if(!lock.tryLock(3,30, TimeUnit.SECONDS))
		{
			return Result.error();
		}
		try
		{
			QueryWrapper oderQW = new QueryWrapper();
			oderQW.eq("coupon_id", coupon_id);
			oderQW.eq("user_id", uid);
			if (seckillOrderService.count(oderQW) > 0) {
				throw new RuntimeException("已抢到");
			}

			/**
			 *	乐观锁处理部分
			 */
			boolean update = seckillActivityService.update().setSql("total_stock = total_stock-1")//必须获取数据库里面的total_stock值-1
					.eq("coupon_id", coupon_id)
					.gt("total_stock", 0)
					.update();//执行
			if (update)
			{
				//创建订单
				SeckillOrder seckillOrder = new SeckillOrder();
				seckillOrder.setOrderSn(redisIdWorker.nextId("coupon"));
				seckillOrder.setUserId(uid);
				seckillOrder.setActivityId(one.getId());
				seckillOrder.setCouponId(coupon_id);

				seckillOrderService.save(seckillOrder);
				return Result.ok();
			}
			return Result.error();
		}
		finally {
			lock.unlock();
		}
		
	}
	
	@Autowired
	RedisTemplate redisTemplate;
	@Autowired
	StringRedisTemplate stringRedisTemplate;
	/**
	 * 优化秒杀，用以上方法效率太低了
	 */
	/**
	 * 将库存信息存到redis中
	 * 
	 */
	private static final String KEY_PREFIX = "stock:";
	@GetMapping("saveStock")
	public Result saveStock(@RequestParam Long coupon_id,@RequestParam Integer total_stock)
	{
	//coupon_id		total_stock
		String key = KEY_PREFIX + "coupon:" + coupon_id;
		String key2 = KEY_PREFIX + "limit";
		
		//获取脚本
		RedisScript<String> redisScript = RedisScript.of(new ClassPathResource("script/saveCouponStockAndSingle.lua"),String.class);
		//执行脚本
		Object result = stringRedisTemplate.execute(
				redisScript,
				Arrays.asList(key,key2),
				total_stock.toString(),new String());

//		System.out.println(result);
		return Result.ok();
	}

	/**
	 * 优化秒杀系统
	 * lua脚本：
	 * 秒杀库存
	 * 判断一人一单
	 * 将优惠卷id和用户id放进阻塞队列
	 */
	@GetMapping("optimizeSeckillCoupon")
	public Result optimizeSeckillCoupon(@RequestParam Long coupon_id, @RequestParam Long uid)
	{
		
		RedisScript<String> of = RedisScript.of(new ClassPathResource("script/seckillSingle.lua"), String.class);
		String key1 = KEY_PREFIX + "coupon:" + coupon_id;
		String key2 = KEY_PREFIX + "limit";
		String key3 = KEY_PREFIX + "queue";

		QueryWrapper qw = new QueryWrapper();
		qw.eq("coupon_id",coupon_id);
		SeckillActivity one = seckillActivityService.getOne(qw);
		SeckillOrder seckillOrder = new SeckillOrder();
		seckillOrder.setOrderSn(redisIdWorker.nextId("coupon"));
		seckillOrder.setUserId(uid);
		seckillOrder.setActivityId(one.getId());
		seckillOrder.setCouponId(coupon_id);
		String s = JSON.toJSONString(seckillOrder);
		Object execute = redisTemplate.execute(of, Arrays.asList(key1, key2,key3),s,uid);
		if(execute != null)
		{
			return Result.error("发生未知错误");
		}
		
		return Result.ok();
	}
	
	@Autowired
	BlockingQueueConsumer blockingQueueConsumer;

	@GetMapping("saveStockRedisStream")
	public Result saveStockRedisStream(@RequestParam Long coupon_id,@RequestParam Integer total_stock)
	{
		//coupon_id		total_stock
		String key = KEY_PREFIX + "coupon:" + coupon_id;
		String key2 = KEY_PREFIX + "limit";

		//获取脚本
		RedisScript<String> redisScript = RedisScript.of(new ClassPathResource("script/saveCouponStockAndSingle.lua"),String.class);
		//执行脚本
		Object result = stringRedisTemplate.execute(
				redisScript,
				Arrays.asList(key,key2),
				total_stock.toString(),new String());

		//开启异步
		blockingQueueConsumer.startConsumingRedisStream();
		
		return Result.ok();
	}

	/**
	 * redis的stream消息队列处理
	 */
	private static final String RKEY_PREFIX = "stream:";
	private static final String GROUP = "mygroup";
	@GetMapping("SeckillCouponRedisStream")
	public Result SeckillCouponRedisStream(@RequestParam Long coupon_id, @RequestParam Long uid)
	{
		RedisScript<String> of = RedisScript.of(new ClassPathResource("script/seckillSingleRS.lua"), String.class);
		String key1 = KEY_PREFIX + "coupon:" + coupon_id;
		String key2 = KEY_PREFIX + "limit";
		String key3 = KEY_PREFIX + "queue";

		QueryWrapper qw = new QueryWrapper();
		qw.eq("coupon_id",coupon_id);
		
		SeckillOrder seckillOrder = new SeckillOrder();
		seckillOrder.setOrderSn(redisIdWorker.nextId("coupon"));
		seckillOrder.setUserId(uid);
		SeckillActivity one = seckillActivityService.getOne(qw);
		seckillOrder.setActivityId(one.getId());
		seckillOrder.setCouponId(coupon_id);
		
		String s = JSON.toJSONString(seckillOrder);
		String order = "order";
		Object execute = redisTemplate.execute(of, Arrays.asList(key1, key2,RKEY_PREFIX+order,GROUP),s,uid);
		if(execute != null)
		{
			return Result.error("发生未知错误");
		}

		return Result.ok();
		
	}
	
	@GetMapping("test")
	public void test()
	{
//		RedisScript<ArrayList> of = RedisScript.of(new ClassPathResource("script/test.lua"), ArrayList.class);
		RedisScript<ArrayList> of = RedisScript.of(new ClassPathResource("script/test.lua"), ArrayList.class);
		Object execute = stringRedisTemplate.execute(of, Collections.emptyList(), "");

		System.out.println(execute);

	}
	
}
